package org.xukai.common.thread;

import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContexts;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Service
public class HttpClientManager implements InitializingBean {
    private final Logger logger = Logger.getLogger(this.getClass());
    private PoolingHttpClientConnectionManager  connectionManager;
    private final long heartbeatPeriod = 10000L;
    private final long initialDelay = 10000L;
    private final TimeUnit unit;
    private int maxTotalConnections;
    private int maxRouteConnections;
    private int connectTimeout;
    private int readTimeout;
    private int connectionRequestTimeout;

    public HttpClientManager() {
        this.unit = TimeUnit.MILLISECONDS;
        this.maxTotalConnections = 400;
        this.maxRouteConnections = 200;
        this.connectTimeout = 10000;
        this.readTimeout = 20000;
        this.connectionRequestTimeout = 10000;
    }

    public void setMaxTotalConnections(int maxTotalConnections) {
        this.maxTotalConnections = maxTotalConnections;
    }

    public void setMaxRouteConnections(int maxRouteConnections) {
        this.maxRouteConnections = maxRouteConnections;
    }

    public void setConnectTimeout(int connectTimeout) {
        this.connectTimeout = connectTimeout;
    }

    public void setReadTimeout(int readTimeout) {
        this.readTimeout = readTimeout;
    }

    public void afterPropertiesSet() throws Exception {
        SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build();
        HostnameVerifier hostnameVerifier = SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,hostnameVerifier);
        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", sslsf)
                .build();
        this.connectionManager =  new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        this.connectionManager.setMaxTotal(this.maxTotalConnections);
        this.connectionManager.setDefaultMaxPerRoute(this.maxRouteConnections);
        SocketConfig socketConfig = SocketConfig.custom().setSoTimeout(readTimeout).build();
        connectionManager.setDefaultSocketConfig(socketConfig);
        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1, new DaemonThreadFactory("httpClient-con-monitor"));
        scheduler.scheduleAtFixedRate(new HttpClientManager.IdleConnectionMonitor(this.connectionManager), 30000L, 30000L, this.unit);
    }

    public HttpClient getHttpClient() {
        return getHttpClient(connectionRequestTimeout, connectTimeout, readTimeout);
    }

    public HttpClient getHttpClient(int connectionRequestTimeout, int connectTimeout, int readTimeout) {
        RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(connectionRequestTimeout)
                .setConnectTimeout(connectTimeout).setSocketTimeout(readTimeout).build();
        return HttpClients.custom().setConnectionManager(connectionManager).setDefaultRequestConfig(requestConfig).build();
    }

    private final class IdleConnectionMonitor implements Runnable {
        PoolingHttpClientConnectionManager connectionManager;

        public IdleConnectionMonitor(PoolingHttpClientConnectionManager connectionManager) {
            this.connectionManager = connectionManager;
        }

        public void run() {
            if(HttpClientManager.this.logger.isDebugEnabled()) {
                HttpClientManager.this.logger.debug("release start connect count:=" + this.connectionManager
                        .getTotalStats().getAvailable());
            }

            this.connectionManager.closeExpiredConnections();
            this.connectionManager.closeIdleConnections((long)(HttpClientManager.this.readTimeout * 2), TimeUnit.MILLISECONDS);
            if(HttpClientManager.this.logger.isDebugEnabled()) {
                HttpClientManager.this.logger.debug("release end connect count:=" + this.connectionManager.getTotalStats().getAvailable());
            }

        }
    }
}
